using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using CodePlex.DnD4eCampaignManager.DnD4e.Enums;
using CodePlex.DnD4eCampaignManager.DnD4e.Interfaces;

namespace CodePlex.DnD4eCampaignManager.DnD4e
{
    public class PlayerCharacter : Combatant, IEquatable<PlayerCharacter>, IPlayerCharacter
    {
        private readonly IPowers _powers;

        public PlayerCharacter(IDefenses defenses, IPowers powers, ISenses senses, IAbilities abilities, IHealingSurges surges, IInitiative initiative,
                               IMovement movement)
        {
            CharacterAlignment = Alignment.Unaligned;
            CharacterGender = Gender.Male;
            CreatureSize = CreatureSize.Medium;
            _defenses = defenses;
            _powers = powers;
            _movement = movement;
            _senses = senses;
            _abilities = abilities;
            _healingSurges = surges;
            _initiative = initiative;
        }

        private PlayerCharacter()
            : this(new Defenses(), new Powers(), new Senses(), new Abilities(), new HealingSurges(new HitPoints()), new Initiative(), new Movement())
        {
            //The existance of this constructor is a consession to the XmlSerializer in the persistence layer.
            //Yes, it makes me feel dirty.  Thanks for asking.
        }

        public override string DisplayName
        {
            get { return Name; }
        }

        public int CharacterAge { get; set; }
        public Alignment CharacterAlignment { get; set; }
        public string CharacterClass { get; set; }
        public string CharacterDeity { get; set; }
        public string CharacterEpicDestiny { get; set; }
        public int CharacterExperiencePoints { get; set; }
        public string CharacterFeats { get; set; }
        public Gender CharacterGender { get; set; }
        public string CharacterHeight { get; set; }

        public int CharacterLevel
        {
            set
            {
                //Ignore this call, it should only be made by the XmlSerializer. . .Another PI Fail. :-(
            }
            get
            {
                if ( CharacterExperiencePoints >= 1000000 )
                {
                    return 30;
                }
                if ( CharacterExperiencePoints >= 825000 )
                {
                    return 29;
                }
                if ( CharacterExperiencePoints >= 675000 )
                {
                    return 28;
                }
                if ( CharacterExperiencePoints >= 550000 )
                {
                    return 27;
                }
                if ( CharacterExperiencePoints >= 450000 )
                {
                    return 26;
                }
                if ( CharacterExperiencePoints >= 375000 )
                {
                    return 25;
                }
                if ( CharacterExperiencePoints >= 310000 )
                {
                    return 24;
                }
                if ( CharacterExperiencePoints >= 255000 )
                {
                    return 23;
                }
                if ( CharacterExperiencePoints >= 210000 )
                {
                    return 22;
                }
                if ( CharacterExperiencePoints >= 175000 )
                {
                    return 21;
                }
                if ( CharacterExperiencePoints >= 143000 )
                {
                    return 20;
                }
                if ( CharacterExperiencePoints >= 119000 )
                {
                    return 19;
                }
                if ( CharacterExperiencePoints >= 99000 )
                {
                    return 18;
                }
                if ( CharacterExperiencePoints >= 83000 )
                {
                    return 17;
                }
                if ( CharacterExperiencePoints >= 69000 )
                {
                    return 16;
                }
                if ( CharacterExperiencePoints >= 57000 )
                {
                    return 15;
                }
                if ( CharacterExperiencePoints >= 47000 )
                {
                    return 14;
                }
                if ( CharacterExperiencePoints >= 39000 )
                {
                    return 13;
                }
                if ( CharacterExperiencePoints >= 32000 )
                {
                    return 12;
                }
                if ( CharacterExperiencePoints >= 26000 )
                {
                    return 11;
                }
                if ( CharacterExperiencePoints >= 20500 )
                {
                    return 10;
                }
                if ( CharacterExperiencePoints >= 16500 )
                {
                    return 9;
                }
                if ( CharacterExperiencePoints >= 13000 )
                {
                    return 8;
                }
                if ( CharacterExperiencePoints >= 10000 )
                {
                    return 7;
                }
                if ( CharacterExperiencePoints >= 7500 )
                {
                    return 6;
                }
                if ( CharacterExperiencePoints >= 5500 )
                {
                    return 5;
                }
                if ( CharacterExperiencePoints >= 3750 )
                {
                    return 4;
                }
                if ( CharacterExperiencePoints >= 2250 )
                {
                    return 3;
                }
                if ( CharacterExperiencePoints >= 1000 )
                {
                    return 2;
                }
                return 1;
            }
        }

        public string CharacterName { get; set; }
        public string CharacterParagonPath { get; set; }
        public string CharacterRace { get; set; }
        public string CharacterRituals { get; set; }
        public int CharacterWeight { get; set; }

        public int FailedDeathSaves
        {
            get { return _healingSurges.FailedDeathSaves; }
            set { _healingSurges.FailedDeathSaves = value; }
        }

        public int HealingSurgesCurrent
        {
            get { return _healingSurges.HealingSurgesCurrent; }
            set { _healingSurges.HealingSurgesCurrent = value; }
        }

        public int HealingSurgesMaximum
        {
            get { return _healingSurges.HealingSurgesMaximum; }
            set { _healingSurges.HealingSurgesMaximum = value; }
        }

        public int HealingSurgeValue
        {
            get { return _healingSurges.HealingSurgeValue; }
            set { _healingSurges.HealingSurgeValue = value; }
        }

        public string PowersAtWill
        {
            get { return _powers.PowersAtWill; }
            set { _powers.PowersAtWill = value; }
        }

        public string PowersDaily
        {
            get { return _powers.PowersDaily; }
            set { _powers.PowersDaily = value; }
        }

        public string PowersEncounter
        {
            get { return _powers.PowersEncounter; }
            set { _powers.PowersEncounter = value; }
        }

        public override bool Equals(object obj)
        {
            if ( ReferenceEquals(null, obj) )
            {
                return false;
            }
            if ( ReferenceEquals(this, obj) )
            {
                return true;
            }
            if ( obj.GetType() != typeof ( PlayerCharacter ) )
            {
                return false;
            }
            return Equals((PlayerCharacter) obj);
        }

        public override string FormatCombatantForDisplay()
        {
            int totalHitPoints = HitPointsCurrent + HitPointsTemporary;
            string temporaryHitPoints = HitPointsTemporary > 0 ? string.Format(" ({0} temp)", HitPointsTemporary) : string.Empty;
            string healingSurges = string.Format("Surges: {0}/{1}; ", HealingSurgesCurrent, HealingSurgesMaximum);
            string movementSeperator = string.IsNullOrEmpty(MovementSpeedOther) ? string.Empty : ",";
            return string.Format("{0} - {1}{7}/{2}; AC: {3}, Fort: {4}, Ref: {5}, Will: {6}; {8}Speed: {9}{11} {10}",
                                 DisplayName, totalHitPoints, HitPointsMaximum, DefenseArmorClass, DefenseFortitude, DefenseReflex, DefenseWillpower,
                                 temporaryHitPoints, healingSurges, MovementSpeed, MovementSpeedOther, movementSeperator).TrimEnd();
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int result = ( _powers != null ? _powers.GetHashCode() : 0 );
                result = ( result * 397 ) ^ CharacterAge;
                result = ( result * 397 ) ^ CharacterAlignment.GetHashCode();
                result = ( result * 397 ) ^ ( CharacterHeight != null ? CharacterHeight.GetHashCode() : 0 );
                result = ( result * 397 ) ^ ( CharacterClass != null ? CharacterClass.GetHashCode() : 0 );
                result = ( result * 397 ) ^ ( CharacterDeity != null ? CharacterDeity.GetHashCode() : 0 );
                result = ( result * 397 ) ^ ( CharacterEpicDestiny != null ? CharacterEpicDestiny.GetHashCode() : 0 );
                result = ( result * 397 ) ^ CharacterExperiencePoints;
                result = ( result * 397 ) ^ ( CharacterFeats != null ? CharacterFeats.GetHashCode() : 0 );
                result = ( result * 397 ) ^ CharacterGender.GetHashCode();
                result = ( result * 397 ) ^ ( CharacterParagonPath != null ? CharacterParagonPath.GetHashCode() : 0 );
                result = ( result * 397 ) ^ ( CharacterName != null ? CharacterName.GetHashCode() : 0 );
                result = ( result * 397 ) ^ ( CharacterRace != null ? CharacterRace.GetHashCode() : 0 );
                result = ( result * 397 ) ^ ( CharacterRituals != null ? CharacterRituals.GetHashCode() : 0 );
                result = ( result * 397 ) ^ CharacterWeight;
                return result;
            }
        }

        public override string ToString()
        {
            MemoryStream memoryStream = new MemoryStream();
            XmlSerializer xmlSerializer = new XmlSerializer(GetType());
            XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF7)
                                              {
                                                  Formatting = Formatting.Indented,
                                                  Indentation = 1,
                                                  IndentChar = '\t'
                                              };
            xmlSerializer.Serialize(xmlTextWriter, this);
            memoryStream = (MemoryStream) xmlTextWriter.BaseStream;
            UTF7Encoding encoding = new UTF7Encoding();
            return encoding.GetString(memoryStream.ToArray()).Replace("><", ">\r\n<");
        }

        public bool Equals(PlayerCharacter obj)
        {
            if ( ReferenceEquals(null, obj) )
            {
                return false;
            }
            if ( ReferenceEquals(this, obj) )
            {
                return true;
            }
            return Equals(obj._powers, _powers) && obj.CharacterAge == CharacterAge && Equals(obj.CharacterAlignment, CharacterAlignment) &&
                   Equals(obj.CharacterHeight, CharacterHeight) && Equals(obj.CharacterClass, CharacterClass) &&
                   Equals(obj.CharacterDeity, CharacterDeity) && Equals(obj.CharacterEpicDestiny, CharacterEpicDestiny) &&
                   obj.CharacterExperiencePoints == CharacterExperiencePoints && Equals(obj.CharacterFeats, CharacterFeats) &&
                   Equals(obj.CharacterGender, CharacterGender) && Equals(obj.CharacterParagonPath, CharacterParagonPath) &&
                   Equals(obj.CharacterName, CharacterName) && Equals(obj.CharacterRace, CharacterRace) &&
                   Equals(obj.CharacterRituals, CharacterRituals) && obj.CharacterWeight == CharacterWeight;
        }

        public void SpendHealingSurge()
        {
            _healingSurges.SpendHealingSurge();
        }

        public static bool operator ==(PlayerCharacter left, PlayerCharacter right)
        {
            return Equals(left, right);
        }

        public static bool operator !=(PlayerCharacter left, PlayerCharacter right)
        {
            return !Equals(left, right);
        }
    }
}